#include "handbiometricdata.h"


/**
 * @file handbiometricdata.cpp
 * @author  Paweł Hanejko <phanejko@gmail.com>
 * @version 1.0
 *
 * @section LICENSE
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details at
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @section DESCRIPTION
 *
 * Plik źródłowy zawiera implementację klasy HandBiometricData,
 * która zawiera dane biometryczne dłoni użytkownika.
 */





const int HandBiometricData::CANNY_THRESH=20;
const int HandBiometricData::BIN_THRESH=111;
const int HandBiometricData::RATIO=20;
const int HandBiometricData::KERNEL_SIZE=3;
const int HandBiometricData::LITTLE_TH=60;
const int HandBiometricData::RING_TH=140;
const int HandBiometricData::MIDDLE_TH=220;
const int HandBiometricData::INDEX_TH=320;
const int HandBiometricData::THUMB_TH=400;
const int HandBiometricData::DIST_MIN=175;

HandBiometricData::HandBiometricData(QString src)
{
    reprocess(src.toStdString().c_str());
}

inline double HandBiometricData::distance(const Point& p1, const Point& p2)const
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}


void HandBiometricData::reprocess(const char *src_path)
{
    // załaduj obraz
    Mat src = imread( src_path );
    if( !src.data ) return;
    Mat dst,src_gray;
    // konwertuj obraz src do skali szarości src_gray
    cvtColor( src, src_gray, CV_BGR2GRAY );
    // stwórz macierz dst tego samego typu i rozmiaru co src
    dst.create( src_gray.size(), src_gray.type() );
    // redukcja szumów z jądrem (KERNEL_SIZE x KERNEL_SIZE)
    blur( src_gray, src_gray, Size(KERNEL_SIZE,KERNEL_SIZE) );
    Mat edges;
    // zapisuje edges z obrazu src_gray wykorzystując algorytm Canny
    // CANNY_THRESH- próg 1, CANNY_THRESH*RATIO - próg 2
    // KERNEL_SIZE- rozmiar macierzy dla operatora Sobela
    Canny( src_gray, edges, CANNY_THRESH, CANNY_THRESH*RATIO, KERNEL_SIZE );

    //znajdowanie konturów w edges
    vector<vector<Point> > contours;
    findContours( edges, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
    //binaryzacja edges
    Mat bin_edges;
    threshold( src_gray, bin_edges, BIN_THRESH, 255, CV_THRESH_BINARY );

    //Znajdowanie konturow w bin_edges
    vector<vector<Point> > bin_contours;
    findContours( bin_edges, bin_contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

    //Szukanie najdluzszego konturu
    vector<Point> max=bin_contours[0];
    for(int i=1; i < bin_contours.size(); i++)
        if (bin_contours[i].size()>max.size())
            max=bin_contours[i];

    //tylko najdluzszy kontur (obrys ręki)
    contours.clear();
    contours.push_back(max);
    dst = Scalar::all(0);
    drawContours( dst, contours, CV_FILLED, (255,255,255), 1 );
    //  wyznaczanie wrist_reference
    Point wrist_reference=contours[0][0];
    int wrist_reference_iterator=0;
    for(int i=1;i<contours[0].size();++i)
    {
        if(wrist_reference.y>contours[0][i].y)continue;
        wrist_reference=contours[0][i];
        wrist_reference_iterator=i;
    }

    Vector<Point> dists; // x- indeks punktu w contours, y- odleglosc od wrist_reference

    // Do dist zapisuję punkty z contours oraz odleglosci zgodnie z ruchem wskazówek zegara zaczynajac od wrist_reference
    // pomijajac punkty ktorych odleglosc od wrist_reference jest mniejsza od DIST_MIN
    for(int i=0;i<=wrist_reference_iterator;++i)
        dists.push_back(Point(wrist_reference_iterator-i,distance(wrist_reference,contours[0][wrist_reference_iterator-i])));
    for(int i=contours[0].size()-1;i>wrist_reference_iterator;--i)
        dists.push_back(Point(i,distance(wrist_reference,contours[0][i])));

    //szukanie maksimów
    int little=0,ring=0,middle=0,index=0,thumb=0;


    for(int i=0;i<dists.size();++i)
    {
        if(dists[i].y<DIST_MIN || contours[0][dists[i].x].x>LITTLE_TH  )continue;
        if(dists[i].y>dists[little].y) little=i;
    }

    for(int i=little;i<dists.size();++i)
    {
        if(dists[i].y<DIST_MIN || contours[0][dists[i].x].x<LITTLE_TH || contours[0][dists[i].x].x>RING_TH)continue;
        if(dists[i].y>dists[ring].y) ring=i;
    }

    for(int i=ring;i<dists.size();++i)
    {
        if(dists[i].y<DIST_MIN || contours[0][dists[i].x].x<RING_TH || contours[0][dists[i].x].x>MIDDLE_TH)continue;
        if(dists[i].y>dists[middle].y) middle=i;
    }

    for(int i=middle;i<dists.size();++i)
    {
        if(dists[i].y<DIST_MIN || contours[0][dists[i].x].x<MIDDLE_TH || contours[0][dists[i].x].x>INDEX_TH)continue;
        if(dists[i].y>dists[index].y) index=i;
    }

    for(int i=index;i<dists.size();++i)
    {
        if(contours[0][dists[i].x].x<INDEX_TH || contours[0][dists[i].x].x>THUMB_TH)continue;
        if(dists[i].y>dists[thumb].y) thumb=i;

    }

    //szukanie minimów

    int little_v=little,ring_v=ring,middle_v=middle,index_v=index;

    for(int i=little;i<ring;++i)
    {
        if(dists[i].y<DIST_MIN)continue;
        if(dists[i].y<dists[little_v].y) little_v=i;
    }
    for(int i=ring;i<middle;++i)
    {
        if(dists[i].y<DIST_MIN)continue;
        if(dists[i].y<dists[ring_v].y) ring_v=i;
    }
    for(int i=middle;i<index;++i)
    {
        if(dists[i].y<DIST_MIN)continue;
        if(dists[i].y<dists[middle_v].y) middle_v=i;
    }
    for(int i=index;i<thumb;++i)
        if(dists[i].y<dists[index_v].y) index_v=i;

    //zaznaczenie wrist_reference i first wrist na obrazie;
    int radius=10;
    Point p,q,tmp,tmp2,wr=wrist_reference;
    wr.x+=radius;
    wr.y-=radius;
    circle(dst, wr, radius, (255,255,255), -1);

    //zaznaczenie koniuszków palców
    p=contours[0][dists[little].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    p=contours[0][dists[ring].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    p=contours[0][dists[middle].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    p=contours[0][dists[index].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    p=contours[0][dists[thumb].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    //zaznaczenie dolinek i linii
    p=contours[0][dists[little_v].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);

    q=contours[0][dists[ring_v].x];
    q.x+=radius/2;
    q.y+=radius/2;
    circle(dst, q, radius, (255,255,255), -1);
    line(dst,p ,q,(255,255,255), 2, 8, 0);
    //narysowanie i wyznaczenie długości linii ring_len
    tmp2.x=(p.x+q.x)/2;
    tmp2.y=(p.y+q.y)/2;
    tmp=contours[0][dists[ring].x];
    tmp.x+=radius/2;
    tmp.y+=radius/2;
    line(dst,tmp,tmp2,(255,255,255), 3, 8, 0);
    ring_len=distance(tmp,tmp2);

    p=contours[0][dists[middle_v].x];
    p.x+=radius/2;
    p.y+=radius/2;
    circle(dst, p, radius, (255,255,255), -1);
    line(dst,q ,p,(255,255,255), 2, 8, 0);
    //narysowanie i wyznaczenie długości linii middle_len
    tmp2.x=(p.x+q.x)/2;
    tmp2.y=(p.y+q.y)/2;
    tmp=contours[0][dists[middle].x];
    tmp.x+=radius/2;
    tmp.y+=radius/2;
    line(dst,tmp,tmp2,(255,255,255), 3, 8, 0);
    middle_len=distance(tmp,tmp2);


    q=contours[0][dists[index_v].x];
    q.x+=radius/2;
    q.y+=radius/2;
    circle(dst, q, radius, (255,255,255), -1);
    line(dst,p ,q,(255,255,255), 2, 8, 0);

    //narysowanie i wyznaczenie długości linii index_len
    tmp2.x=p.x+fabs(p.x-q.x)/4;
    tmp2.y=p.y+fabs(p.y-q.y)/4;;
    tmp=contours[0][dists[index].x];
    tmp.x+=radius/2;
    tmp.y+=radius/2;
    line(dst,tmp,tmp2,(255,255,255), 3, 8, 0);
    index_len=distance(tmp,tmp2);

    line(dst,q ,wr,(255,255,255), 2, 8, 0);

    //narysowanie i wyznaczenie długości linii thumb_len
    tmp2.x=(q.x+wr.x)/2;
    tmp2.y=(q.y+wr.y)/2;
    tmp=contours[0][dists[thumb].x];
    tmp.x+=radius/2;
    tmp.y+=radius/2;
    line(dst,tmp,tmp2,(255,255,255), 3, 8, 0);
    thumb_len=distance(tmp,tmp2);



    char src_processed_path[500];
    strcpy(src_processed_path,src_path);
    strcat(src_processed_path,"_processed.jpeg");
    imwrite(src_processed_path,dst);


}
